#ifndef __FRAMEWORK_H__
#define __FRAMEWORK_H__

// MACROS
#define glClearColor3v(f) glClearColor((f)[0], (f)[1], (f)[2], 0.0)

#include <GL/glut.h>
#include <string>
#include <GL/freeglut.h>


class IFrameworkCallbacks{
	public:
		IFrameworkCallbacks(){}
		virtual ~IFrameworkCallbacks(){}


		virtual void init(){}
		virtual void display()=0;
		virtual void timer(int value){}
		virtual void reshape(int oldW,int oldH,int w,int h){}
		virtual void keyboard(unsigned char key,int x ,int y){}
		virtual void specKeyboard(int key,int x ,int y){}
		virtual void mousePressed(int key,int state,int x ,int y){}
		virtual void mouseMotion(int x ,int y){}
		virtual void mouseEntry(int state){}
		virtual void mousePassiveMotion(int x,int y){}
};

class Framework{
	private:
		IFrameworkCallbacks* callbacks;
		int windowHeight;
		int windowWidth;
		int windowX;
		int windowY;
		int window;
		std::string caption;
		bool timerSet;
		unsigned int msecTimer;

	public:
		~Framework(){}

		static Framework& instance(){
			static Framework framework;
			return framework;
		}

		static void display(){
			Framework::instance().getCallbacks()->display();

			///
			glutSwapBuffers();
			///
		}

		static void reshape(int w, int h){
			Framework::instance().getCallbacks()->reshape(Framework::instance().windowWidth,Framework::instance().windowHeight,w,h);

			Framework::instance().windowHeight = h;
			Framework::instance().windowWidth = w;
		}

		static void keyboard(unsigned char key,int x,int y){
			Framework::instance().getCallbacks()->keyboard(key,x,y);
		}

		static void specialFunc(int key,int x ,int y){
			Framework::instance().getCallbacks()->specKeyboard(key,x,y);
		}

		static void mouseFunc(int button,int state,int x ,int y){
			Framework::instance().getCallbacks()->mousePressed(button,state,x,y);
		}

		static void mouseMotion(int x ,int y){
			Framework::instance().getCallbacks()->mouseMotion(x,y);
		}

		static void mouseEntryFunc(int state){
			Framework::instance().getCallbacks()->mouseEntry(state);
		}

		static void mousePassiveMotion(int x,int y){
			Framework::instance().getCallbacks()->mousePassiveMotion(x,y);
		}

		static void timer(int value){
			Framework& fr = Framework::instance();
			fr.getCallbacks()->timer(value);
			if(fr.isTimerSet())
				glutTimerFunc(fr.getTimerMsec(),Framework::timer,1);
		}

		bool isTimerSet(){
			return timerSet;
		}

		unsigned int getTimerMsec(){
			return msecTimer;
		}


		void setWindowDimensions(int windowHeigth,int windowWidth,int windowX,int windowY){
			this->windowHeight = windowHeigth;
			this->windowWidth = windowWidth;
			this->windowX = windowX;
			this->windowY = windowY;
		}

		void setClearColor(float r,float g,float b){
			glClearColor(r,g,b,0.0);
		}

		void setCaption(const std::string& caption){
			this->caption = caption;
		}

		void setTimer(unsigned int msec){
			this->timerSet = true;
			this->msecTimer = msec;
		}

		void unsetTimer(){
			this->timerSet = false;
		}

		void setCallbacks(IFrameworkCallbacks* callbacks){
			this->callbacks = callbacks;
		}

		void redisplay(){
			glutPostWindowRedisplay(this->window);
		}

		int getWindow(){
			return this->window;
		}

		float getWindowWidth(){
			return this->windowWidth;
		}

		float getWindowHeight(){
			return this->windowHeight;
		}

		void mainLoop(int argc,char** argv){
			glutInit(&argc, argv);
			glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
			glutInitWindowSize (windowWidth, windowHeight);
			glutInitWindowPosition (windowX, windowY);
			window = glutCreateWindow (caption.c_str());
			callbacks->init();
			glutKeyboardFunc(Framework::keyboard);
			glutDisplayFunc(Framework::display);
			glutSpecialFunc(Framework::specialFunc);
			glutReshapeFunc(Framework::reshape);
			glutMouseFunc(Framework::mouseFunc);
			glutEntryFunc(Framework::mouseEntryFunc);
			glutMotionFunc(Framework::mouseMotion);
			glutPassiveMotionFunc(Framework::mousePassiveMotion);
			if(isTimerSet())
				glutTimerFunc(msecTimer,Framework::timer,1);
			glutFullScreen();
			glutMainLoop();
		}

	private:
		Framework(){
			this->setWindowDimensions(800,800,0,0);
			this->setCaption("");
			this->unsetTimer();
		}

		Framework(const Framework& framework);
		void operator=(const Framework& framework);

		IFrameworkCallbacks* getCallbacks(){ return callbacks; }
};

#endif // __FRAMEWORK_H__
